#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>

// 模块信息描述
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luo");
MODULE_DESCRIPTION("This is a simple example of kthread");
MODULE_VERSION("1.0");

// 使用线程函数的数据
// task_struct指针用于在创建线程时接收返回值，因为在初始化函数中创建，在退出函数中销毁，所以需要定义成全局变量
static struct task_struct *mythread = NULL; 
static int thread_data = 12345;

// 线程函数编写
// 功能：线程中每迭代一次需要睡眠1秒，同时统计线程的迭代次数
static int mythread_fun(void* data) {
    // 读出数据
    int* dataval = (int*)data;
    int count = 0;
    
    pr_info("thread_fun: kernel thread starts with data, %d\n", *dataval);

    // 进行线程迭代
    while (!kthread_should_stop()) {
        // 打印信息说明进行迭代
        pr_info("thread_iteration: %d", count);
        ++count;

        // 进程进行睡眠，可中断睡眠1秒
        // 因为在中断的过程中可能会收到内核线程停止的信号
        set_current_state(TASK_INTERRUPTIBLE);
        schedule_timeout(msecs_to_jiffies(1000));

        if (kthread_should_stop()) {
            break;
        }
    }

    // 线程退出
    pr_info("thread_fun: kernel thread exit\n");
    return 0;
}

static int __init mythread_init(void) {
    pr_info("thread module: initializing kthread example module\n");
    // 创建内核线程
    const char* name = "mythread";
    pr_info("thread module: creating kthread: %s\n", name);
    mythread = kthread_create(mythread_fun, &thread_data, name);
    if (IS_ERR(mythread)) {
        pr_err("thread module: failed to create kthread: %ld\n", PTR_ERR(mythread));
        // 打印错误信息后该干什么？直接返回错误码？
        // 需要先释放资源
        return PTR_ERR(mythread);
    }

    // 唤醒内核线程
    pr_info("thread module: waking kthread\n");
    wake_up_process(mythread);
    return 0;
}

static void __exit mythread_exit(void) {
    pr_info("thread module: exiting kthread module\n");

    // 判断内核线程是否创建，根据创建情况进行不同退出行为
    if(mythread && !IS_ERR(mythread)) {
        pr_info("stopping kthread...\n");
        int ret = kthread_stop(mythread);
        pr_info("kthread stop with exit code: %d\n", ret);
    }
    else {
        pr_info("NO kernel thread to stop\n");
    }
    mythread = NULL;
}

module_init(mythread_init);
module_exit(mythread_exit);